package SevenZip.Compression.RangeCoder;
import java.io.IOException;

public class Encoder
{
	static final int kTopMask = ~((1 << 24) - 1);
	
	static final int kNumBitModelTotalBits = 11;
	static final int kBitModelTotal = (1 << kNumBitModelTotalBits);
	static final int kNumMoveBits = 5;
	
	java.io.OutputStream Stream;

	long Low;
	int Range;
	int _cacheSize;
	int _cache;
	
	long _position;
	
	public void SetStream(java.io.OutputStream stream)
	{
		Stream = stream;
	}
	
	public void ReleaseStream()
	{
		Stream = null;
	}
	
	public void Init()
	{
		_position = 0;
		Low = 0;
		Range = -1;
		_cacheSize = 1;
		_cache = 0;
	}
	
	public void FlushData() throws IOException
	{
		for (int i = 0; i < 5; i++)
			ShiftLow();
	}
	
	public void FlushStream() throws IOException
	{
		Stream.flush();
	}
	
	public void ShiftLow() throws IOException
	{
		int LowHi = (int)(Low >>> 32);
		if (LowHi != 0 || Low < 0xFF000000L)
		{
			_position += _cacheSize;
			int temp = _cache;
			do
			{
				Stream.write(temp + LowHi);
				temp = 0xFF;
			}
			while(--_cacheSize != 0);
			_cache = (((int)Low) >>> 24);
		}
		_cacheSize++;
		Low = (Low & 0xFFFFFF) << 8;
	}
	
	public void EncodeDirectBits(int v, int numTotalBits) throws IOException
	{
		for (int i = numTotalBits - 1; i >= 0; i--)
		{
			Range >>>= 1;
			if (((v >>> i) & 1) == 1)
				Low += Range;
			if ((Range & Encoder.kTopMask) == 0)
			{
				Range <<= 8;
				ShiftLow();
			}
		}
	}
	
	
	public long GetProcessedSizeAdd()
	{
		return _cacheSize + _position + 4;
	}
	
	
	
	static final int kNumMoveReducingBits = 2;
	public static final int kNumBitPriceShiftBits = 6;
	
	public static void InitBitModels(short []probs)
	{
		for (int i = 0; i < probs.length; i++)
			probs[i] = (kBitModelTotal >>> 1);
	}
	
	public void Encode(short []probs, int index, int symbol) throws IOException
	{
		int prob = probs[index];
		int newBound = (Range >>> kNumBitModelTotalBits) * prob;
		if (symbol == 0)
		{
			Range = newBound;
			probs[index] = (short)(prob + ((kBitModelTotal - prob) >>> kNumMoveBits));
		}
		else
		{
			Low += (newBound & 0xFFFFFFFFL);
			Range -= newBound;
			probs[index] = (short)(prob - ((prob) >>> kNumMoveBits));
		}
		if ((Range & kTopMask) == 0)
		{
			Range <<= 8;
			ShiftLow();
		}
	}
	
	private static int[] ProbPrices = new int[kBitModelTotal >>> kNumMoveReducingBits];
	
	static
	{
		int kNumBits = (kNumBitModelTotalBits - kNumMoveReducingBits);
		for (int i = kNumBits - 1; i >= 0; i--)
		{
			int start = 1 << (kNumBits - i - 1);
			int end = 1 << (kNumBits - i);
			for (int j = start; j < end; j++)
				ProbPrices[j] = (i << kNumBitPriceShiftBits) +
						(((end - j) << kNumBitPriceShiftBits) >>> (kNumBits - i - 1));
		}
	}
	
	static public int GetPrice(int Prob, int symbol)
	{
		return ProbPrices[(((Prob - symbol) ^ ((-symbol))) & (kBitModelTotal - 1)) >>> kNumMoveReducingBits];
	}
	static public int GetPrice0(int Prob)
	{ 
		return ProbPrices[Prob >>> kNumMoveReducingBits]; 
	}
	static public int GetPrice1(int Prob)
	{ 
		return ProbPrices[(kBitModelTotal - Prob) >>> kNumMoveReducingBits]; 
	}
}
